/* 
CPSC 323 - Lexical Analysis
Katie Soto
Chelsea Gunderson
2011
*/

//********HEADERS, FUNCTIONS, AND VARIABLES:*************
#include "LexicalAnalyzer.h" //this header includes many variables 
							 //and functions required
								//for the lexical analyzer to function.

string lexer(int& current);	//MAIN FUNCTION: returns the next token in sequence.
void writeLex();			//write lexeme/tokens to output file.
void openFile();			//open file, populate buffer[] with file contents

ofstream resultsFile;		//output file containing Syntax Analysis (project 2)
ofstream resultsFile_Asmb;  //output file containing Assembly code (project 3)
int current;				//the current marker when reading in the file
string nextToken;
int dontpass;				//marks the end of the file
int numberOfTokens;
int lineNumber;				//line numbers start at 1.
string errorRecovery;		//stores the user's error recovery setting
int numOfErrors;
#include "Assembly.h"
#include "Syntax.h"  //top down parser - Recursive Descent Parser (RDP)


//********FUNCTION DEFINITIONS:*************
//main()
//-purpose: program starts here. it opens the file, then calls the 
//lexer to get a token one at a time. Syntax is analyzed, and
//assembly code is generated.
int main()
{
//***Initialize Project 3 (Assembly Generation) variables:
	Initialize();
	lineNumber=1;	//line numbers start at 1.
	numOfErrors=0;
//***Open Files**
	openFile();
//***Error Recovery Setting**	
	cout<<"Exit on any error found? 'y' for yes, 'n' for no: ";
	cin>>errorRecovery;
	if((errorRecovery!="y")&&(errorRecovery!="n"))
	{
		cout<<"Input for error recovery not recognized. Will use default: Exit on errors."<<endl;
	}else if(errorRecovery=="y")
	{
		cout<<"Program will exit after any errors found"<<endl;
	}else if(errorRecovery=="n")
	{
		cout<<"Program will try to continue after any errors found"<<endl;
	}
//***start the lexical analysis and syntax analysis loop**
	dontpass=length;	//marker to mark where counter should stop (don't look at 
						//buffer[counter+1] when it's end of file!)
	numberOfTokens=0;
	//this 'for' loop will give you the next token one at a time.
	//it then prints the tokens in order to the console
	current=0;
	cout<<"Starting Lexical Analysis, Syntax Analysis, and Generating Assembly Code...\n\n*****************"<<endl;
	//note: this for loop really only runs once:
		//gets first token,
		//runs Rat11f. Rat11F is the start of the rules for syntax analysis
		//   and continues through the whole file, incrementing current
		//   as it goes. So really, this loop runs just once:
	for(current; current < length; current++)
			{
			//Assignment 1 (Lexical Analysis) start
			//FIXED from project 2: if input starts with new line, the program will now work correctly.
			nextToken=lexer(current);
				while(nextToken=="ignore"){//if the token is one that we ignore, 
							//keep getting the next one until 
							//it's one that we DONT ignore
					current++;
					nextToken=lexer(current);
				}//once you have one that ISN'T "ignore", then print it out:
				if(nextToken!="ignore")
				{
					numberOfTokens++;	//number of tokens is not needed, 
										//but its interesting to know
				}
			//Assignment 2 (Syntax Analysis) START. 
			//Note that this also includes starting Assignment 3 (Assembly Code generation)
			Rat11F();	//starting symbol
			}
	print_out_SymTable();
	print_out_InstrTable();

	resultsFile.close();
	resultsFile_Asmb.close();
	cout<<"Results have been printed to AssemblyOutput.txt and SyntaxOutput.txt"<<endl;
	cout<<"\ntype \"q\" to quit: ";
	string answer;
	cin>>answer;
	if(answer=="q")
	{	//quit
		exit(0);
		return 0;
	}
}


//openFile()
//-purpose: opens output files for writing. opens input.txt for reading.
//			finds the length of the input file, puts all the characters into
//			an the array: buffer[] then closes the input file.
//-input: none
//-output: none
	void openFile()
	{
	string myFile;
	cout<<"Where is your input file located? (default is input.txt): ";
	getline( cin, myFile );
	//output file "SyntaxOutput.txt" contains the Syntax Analysis
	//(Rules and errors)
		resultsFile.open ("SyntaxOutput.txt");
	//output file "AssemblyOutput.txt" contains the Assembly Code
	//generated (project 3)
		resultsFile_Asmb.open ("AssemblyOutput.txt");

	//opens the input file specified
	FILE *inputFile;
	inputFile=fopen(myFile.c_str(),"rb");
	if (inputFile==NULL)
	{
		cout<<"Unable to open "<<myFile<<", using default: input.txt"<<endl;
		inputFile=fopen("input.txt","rb");
	}else{
		cout<<"Successfully opened "<<myFile<<endl;
	}
	fseek(inputFile,0,SEEK_END); //go to end
	length=ftell(inputFile); //get position at end
	fseek(inputFile,0,SEEK_SET); //go to beginning
	buffer=(char *)malloc(length); //create the buffer[] of the size of the file
	fread(buffer,length,1,inputFile); //read into bufferfer
	fclose(inputFile);
	}

//writeLex()
//-purpose: writes the current token and lexeme to the output file
//-input: none
//-output: none
void writeLex()
{
resultsFile <<setw(10)<<left<<"Token: "<< resultsStruct.token <<setw(10)<<left<< "\t\tLexeme: " << resultsStruct.lexeme <<setw(10)<<left<<" \t\t Line Number:"<<lineNumber<<endl;
}

//lexer()
//-purpose: given the start of a character sequence(buffer[current]), 
//			the lexer determines if the character seqeunce forms a 'seperator',
//			'operator', 'keyword', 'identifier', 'integer', or 'real'. 
//-input: current-the index of the current character in the buffer array 
//			that we are looking at
//-output: the next token in the buffer array
//-calls: this function calls	see top of LexicalAnalyzer.h for descriptions:
//								bool is_keyword( string checkString );	
//								bool is_operator( char checkChar );		
//								bool is_seperator( char checkChar );	
//									int char_to_col_id(char w);				
//								bool is_identifier( string w );			
//									int char_to_col_int(char w);			
//								int is_int_or_real( string w );
string lexer(int& current){
	string currentString;
	string next;
                //is it a seperator? if the current character is a seperater.. 
				//AND the next one isn't '=' ALSO checks if the NEXT character 
				//isn't =, for ":=" which is an OPERATOR...
                        if(is_seperator(buffer[current]))
                        {
							if(current!=dontpass){	//check current+1 only if 
													//there IS a current+1 
													//(not end of file)
								if((buffer[current]==':')&&(buffer[current+1]== '=')){
										//then it's :=
                                        currentString=buffer[current];  
										//currentString = :
                                        next = buffer[current+1];
                                        currentString.append(next); 
										//currentString = :=
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="operator";
                                        writeLex();
                                        current++;	//took care of 2 characters (:=)
										return resultsStruct.token;
								}}
							if(buffer[current]==' '){
                                                //spaces should still be considered a 
												//seperator because function bb is not
												//functionbb.. but if we get a space,
												//just IGNORE it: don't store or 
												//output it
                                        //Ignore    
										return "ignore";
                            }else{  //it's just a plain seperator
                                        currentString=buffer[current];
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="seperator";
                                        writeLex();
										return resultsStruct.token;
                                }
                        }else if(is_operator(buffer[current])){
                //is it an operator?
                                //check if the character is an operator.
                                //also check the NEXT character to see if it's a
								//longer operator.. (= and =>)
                                //some operators that are two characters: <= := /= =>
                            if(current!=dontpass){	//check current+1 only if there 
													//IS a current+1 (not end of file)
								if((buffer[current]=='<')&&(buffer[current+1]== '='))
                                {
                                        currentString=buffer[current];  
										//currentString = <
                                        next = buffer[current+1];
                                        currentString.append(next); 
										//currentString = <=        
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="operator";
                                        writeLex();
                                        current++; //took care of 2 characters (<=)
										return resultsStruct.token;
                                }else if((buffer[current]==':')&&(buffer[current+1]== '='))
                                {
                                        currentString=buffer[current];  
                                        next = buffer[current+1];
                                        currentString.append(next);
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="operator";
                                        writeLex();
                                        current++;	//took care of 2 characters (:=)
										return resultsStruct.token;
                                }else if((buffer[current]=='/')&&(buffer[current+1]== '='))
                                {
                                        currentString=buffer[current];  
                                        next = buffer[current+1];
                                        currentString.append(next);
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="operator";
                                        writeLex();
                                        current++;	//took care of 2 characters (:=)
										return resultsStruct.token;
                                }else if((buffer[current]=='=')&&(buffer[current+1]== '>'))
                                {
                                        currentString=buffer[current];  
                                        next = buffer[current+1];
                                        currentString.append(next);
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="operator";
                                        writeLex();
                                        current++;	//took care of 2 characters (=>)
										return resultsStruct.token;
                                }
							}
							//else, it's just a plain operator
                                        currentString=buffer[current];
                                        resultsStruct.lexeme=currentString;
                                        resultsStruct.token="operator";
                                        writeLex();
										return resultsStruct.token;

                        }else if(isalpha(buffer[current])){
                //is it a letter?
								
                                //keep adding to the string until you hit 
								//anything but a letter or underscore
                                //ex:  p -> pr -> pri -> prin -> prin( STOP
								// because of the '('
                                currentString=buffer[current];  //p
								current++; 
								//while we still have a character to look at 
								//(current !=dontpass), meaning, we havn't
								//reached the end of file, check to see if the next 
								//character is still a letter or '_'
								//basically check current+1 only if there IS a 
								//current+1 (not end of file)
								while(  (current!=dontpass)&& ((isalpha(buffer[current])) || (buffer[current]=='_'))  )  
                                {
									    next = buffer[current];         //next = r
                                        currentString.append(next);     //p+r
                                        current++;
                                }
								//***FIXED FROM PROJECT 1:***
								//"Rat11" is not a valid identifier
								if((current!=dontpass)&&(isdigit(buffer[current])))
								{
									//if the next character is a digit and not a seperator or operator,
									//ex:
										//"Rat11" and not "Rat 11"
									next = buffer[current];         //next = r
                                    currentString.append(next);     //p+r
                                    current++;
									cout<<"ERROR: Lexical error- "<<currentString<<" is not a valid identifier or keyword"<<endl;
								}
								//if the identifier doesn't have a digit after 
								//it, it's ok, its valid, continue
								current--;
                                //if ch is a letter, go to FSM1 which is a finite
								//state machine for our 'identifiers'. once it's
								//done, check to see if the 'identifier' is in 
								//the 'keywords' table.
                                //      if it's in the 'keywords' table, it is 
								//				actually a 'keyword'
                                //      if not, it is an 'identifier'
                                        if(is_identifier(currentString)){
                                                //if it's an approved identifier, 
												//check to see if it's actually
												//a keyword
                                                if(is_keyword(currentString)){
                                                        resultsStruct.lexeme=currentString;
                                                        resultsStruct.token="keyword";
                                                        writeLex();
														return resultsStruct.token;
                                                }else{ //not a keyword, so it's an 
														//identifier
                                                        resultsStruct.lexeme=currentString;
                                                        resultsStruct.token="identifier";
                                                        writeLex();
														return resultsStruct.token;
                                                }
                                        }else if(!(is_identifier(currentString)))
										{
											cout<<"Error: Lexical error-"<<currentString<<" is not a valid identifier or keyword"<<endl;
										}else{ //it started with a letter, but it's 
												//not a keyword nor identifier.. 
												//so unknown:
                                                        resultsStruct.lexeme=currentString;
                                                        resultsStruct.token="unknown";
                                                        writeLex();
														return resultsStruct.token;
                                        }
                        }else if(isdigit(buffer[current])||buffer[current]=='.'){
						//***FIXED FROM PROJECT 1:***
						//".123" is now considered a valid real.
                //is it a digit?
                                //keep adding to the string until you hit anything 
								//but a number or period
                                //ex:  5 -> 52 -> 52. -> 52.1 -> 52.1# STOP 
								//  because of the '#'
                                currentString=buffer[current];
                                
								current++;
								//while we still have a character to look at 
								//(current !=dontpass), meaning, we havn't
								//reached the end of file, check to see if the 
								//next character is still a digit or ','...
								//basically check current+1 only if there IS 
								//a current+1 (not end of file)
                                while( (current!=dontpass)&& ((isdigit(buffer[current]))||(buffer[current]=='.')))	
                                {
                                        next = buffer[current];
                                        currentString.append(next);
                                        current++;
                                }
								current--;
							 
                                //if ch is a digit, go to FSM2 which is a finite 
								//state machine for our 'reals' and 'integers'
                                //      if it's in the 'real' accepting state, 
								//			it's a real.
                                //      if it's in the 'integers' accepting state, 
								//			it's an integer.
                                if((is_int_or_real(currentString))==1){ 
												//its an int
                                                resultsStruct.lexeme=currentString;
                                                resultsStruct.token="int";
                                                writeLex();
												return resultsStruct.token;
                                }else if((is_int_or_real(currentString))==3){ 
												//its a real
                                                resultsStruct.lexeme=currentString;
                                                resultsStruct.token="real";
                                                writeLex();
												return resultsStruct.token;
                                }else{ //it started with a digit, but it's not an 
										//int nor real.. so unknown:
                                                resultsStruct.lexeme=currentString;
                                                resultsStruct.token="unknown";
                                                writeLex();
												return resultsStruct.token;
                                }
				//the bottom here collectively is "whitespace", which 
				//is acknowledged but ignored.
                        }else if((buffer[current]=='\\n')||(buffer[current]== '\\r')||(buffer[current]=='CRLF'))
                        {
							//never goes here
								return "ignore";
                        }else if(buffer[current] == EOF){
							//never goes here
                                return "ignore";
						}else if(buffer[current] == '\n'){
							//we have hit a new line. Note the line number!
							//useful for error reports.
							//line numbers start at 1.
							lineNumber++;
								return "ignore";
						}else{
							//everything else ignore
							return "ignore";
                        }     
//everything else, ignore
return "ignore";
}//end lexer